//
// ClockIRQAction.cpp
// Usage: Clock interrupt(IRQ0) action
// Create Date: 2009-04-20
// Creator: MaJiuyue
//
#include <NewLife.h>
#include <chips.h>
#include <SCI.h>
#include "ClockIRQAction.h"

ClockIRQAction * ClockIRQAction::instance = NULL;
ClockIRQAction * ClockIRQAction::GetInstance()
{
	if(!instance)
		instance = new ClockIRQAction();
	return instance;
}

ClockIRQAction::ClockIRQAction()
{
	time_t tm = GetCMOSTime();
	kprintf("System startup time: %s", ctime(&tm));
}

void ClockIRQAction::operator() (u32 irq, regs_t *regs)
{
	// Reopen IRQ 0
	__asm__ __volatile__("pushf\n\t"
		"inb %%dx, %%al\n\t"
		"and $0xFE, %%al\n\t"
		"outb %%al, %%dx\n\t"
		"popf"
		::"d"(PIC_M_CTLMASK)
	);

	Callout * current = (Callout *)m_CalloutListHead.getFirst();
	if(current!=NULL)
	{
		--current->m_remain;
		while( current!=NULL && current->m_remain==0 )
		{
			m_CalloutListHead.removeFirst();
			(*current)();
			current = m_CalloutListHead.getFirst();
		}
	}

	
}

void ClockIRQAction::AddCallout(Callout * callout)
{
	// First, set remained time equals delay time
	callout->m_remain = callout->m_delay;

	// If callout list is empty, just insert first. 
	// If not, find a suitable position. 
	if(m_CalloutListHead.getLength()==0)
		m_CalloutListHead.insertFirst(callout);
	else
	{
		Callout * current = m_CalloutListHead.iterateFirst();
		while(m_CalloutListHead.iterateIsValid())
		{
		//kprintf("[%d, %d]", callout->m_remain-current->m_remain, callout->m_remain <= current->m_remain);
			// Found a suitable place, insert before that position
			if(callout->m_remain <= current->m_remain)
			{
				// insert callout before current
				m_CalloutListHead.iterateInsertBefore(callout);

				// decrease current element's remained time
				current->m_remain -= callout->m_remain;
				//kprintf("{%d,%d}", callout->m_remain, current->m_remain);
				
				// all work done, return
				return;
			}

			// decrease callout's remained time
			callout->m_remain -= current->m_remain;
			// iterate move next
			current = m_CalloutListHead.iterateNext();
		}

		// Come here, so append callout to the list's tail
		m_CalloutListHead.insertLast(callout);
	}
}

void ClockIRQAction::RemoveCallout(Callout * callout)
{
	Callout * current = m_CalloutListHead.iterateFirst();
	while(m_CalloutListHead.iterateIsValid())
	{
		if(current == callout)
		{
			Callout * next = m_CalloutListHead.iterateNext();
			if(next!=NULL)
				next->m_remain += current->m_remain;
			m_CalloutListHead.iteratePrev();
			m_CalloutListHead.remove();
			break;
		}

		current = m_CalloutListHead.iterateNext();
	}
}

#define RTC_ALWAYS_BCD	1
time_t ClockIRQAction::GetCMOSTime()
{
	unsigned int year, mon, day, hour, min, sec; 
	struct tm time;
	int i;

	/* The Linux interpretation of the CMOS clock register contents: 
	* When the Update-In-Progress (UIP) flag goes from 1 to 0, the 
	* RTC registers show the second which has precisely just started. 
	* Let's hope other operating systems interpret the RTC the same way. 
	*/ 
	/* read RTC exactly on falling edge of update flag */ 
	for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ 
		if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) 
			break; 
	for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ 
		if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) 
			break; 
	
	do { /* Isn't this overkill ? UIP above should guarantee consistency */ 
		sec = CMOS_READ(RTC_SECONDS); 
		min = CMOS_READ(RTC_MINUTES); 
		hour = CMOS_READ(RTC_HOURS); 
		day = CMOS_READ(RTC_DAY_OF_MONTH); 
		mon = CMOS_READ(RTC_MONTH); 
		year = CMOS_READ(RTC_YEAR); 
	} while (sec != CMOS_READ(RTC_SECONDS)); 
	if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 
	{ 
		BCD_TO_BIN(sec); 
		BCD_TO_BIN(min); 
		BCD_TO_BIN(hour); 
		BCD_TO_BIN(day); 
		BCD_TO_BIN(mon); 
		BCD_TO_BIN(year); 
	} 
	if ((year += 1900) < 1970) 
		year += 100; 

	time.tm_year = year - 1900;
	time.tm_mon = mon - 1;
	time.tm_mday = day ;
	time.tm_hour = hour;
	time.tm_min = min;
	time.tm_sec = sec;

	return mktime(&time); 
}


